// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: cpu_exec0.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
#include "bcore/InstructionEmulator.h"
#include "InstructionWord.h"
#include <cassert>
#include "Sf/Sf_Trap.h"
#include "V9/V9_ArchState.h"
#include "Hv/Hv_ArchState.h"

using namespace std;
using namespace Riesling;

#define NO_CC2_ICC 0
#define NO_CC2_XCC 2

//
//  cpu_exec0.cc
//  -------------- 
//  Contains routines and data for BRANCH instruction (OP0) 
//  implementation
//

#undef  BN
#undef  BA
#define BN 2  // special
#define BA 3  // case

static uint8_t bpr_taken[8][4] =
{
  //   0  < 0  > 0
   BN, BN, BN, BN,
   BN,  1,  0,  0,
   BN,  1,  1,  0,
   BN,  0,  1,  0,

   BN, BN, BN, BN,
   BN,  0,  1,  1,
   BN,  0,  0,  1,
   BN,  1,  0,  1,
};

static uint8_t fbr_taken[16][4]  =
{

// E  L   G   U

  BN, BN, BN, BN, 
  0,  1,  1,  1,
  0,  1,  1,  0,
  0,  1,  0,  1,
  0,  1,  0,  0,
  0,  0,  1,  1,
  0,  0,  1,  0,
  0,  0,  0,  1,

  BA, BA, BA, BA,  
  1,  0,  0,  0,
  1,  0,  0,  1,
  1,  0,  1,  0,
  1,  0,  1,  1,
  1,  1,  0,  0,
  1,  1,  0,  1,
  1,  1,  1,  0,
};

static uint8_t br_taken [16][16]  = 
{
   BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN,

        0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1,
        0,0,1,1, 1,1,1,1, 1,1,0,0, 1,1,1,1,
        0,0,1,1, 0,0,1,1, 1,1,0,0, 1,1,0,0,
        0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,
        0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
        0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
        0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,

   BA, BA, BA, BA,  BA, BA, BA, BA,  BA, BA, BA, BA,  BA, BA, BA, BA,

        1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,
        1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,
        1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,
        1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,
        1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,
        1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
        1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,
};

//int 
//exec_bcc_err (cpu_structT* sp, Word instr, LWord instr_pc);

//////////////////////////////////////////////////////

void InstructionEmulator::exec_bicc_core ( InstructionWord &i, int taken ) 
{
    // checkTrapBeforeCommit(i);

    int32_t  disp;
    int64_t  delta;
    uint64_t pc  = as_->getPc();		// temp holding value
    
    if (taken == 1) 
    {
        ctiTaken_ = true;
        if (!pstate_tct_enabled_)
        {
            ctiExec_ = true;
            disp = i.disp22();
            delta = ((disp << 10) >> 8);
            as_->setAnnulled( false );    // cannot annul on taken branch
            as_->setPc( as_->getNpc() );
            as_->setNpc( pc + delta );
        }
    } 
    else if (taken == BA) 
    {
        ctiTaken_ = true;
        if (!pstate_tct_enabled_)
        {
            ctiExec_ = true;
            disp = i.disp22();
            delta = ((disp << 10) >> 8);
            as_->setAnnulled( i.a() );
            as_->setPc( as_->getNpc() );
            as_->setNpc( pc + delta );
        }
    } 
    else 
    {
        uint64_t npc = as_->getNpc();
        ctiExec_ = true;
	as_->setAnnulled( i.a() );
        as_->setPc( npc );
        as_->setNpc( npc + 4 );
    }
}

//////////////////////////////////////////////////////

void InstructionEmulator::exec_bpcc_core ( InstructionWord &i, int taken ) 
{
    // checkTrapBeforeCommit(i);

    int32_t  disp;
    int64_t  delta;
    uint64_t pc  = as_->getPc();		// temp holding value
  
    if (taken == 1) 
    {
        ctiTaken_  = true;
        if (!pstate_tct_enabled_)
        {
            ctiExec_ = true;
            disp = i.disp19();
            delta = ((disp << 13) >> 11);
            as_->setAnnulled( false );    // cannot annul on taken branch
            as_->setPc( as_->getNpc() );
            as_->setNpc( pc + delta );
        }
    } 
    else if (taken == BA) 
    {
        ctiTaken_  = true;
        if (!pstate_tct_enabled_)
        {
            ctiExec_ = true;
            disp = i.disp19();
            delta = ((disp << 13) >> 11);
            as_->setAnnulled( i.a() );
            as_->setPc( as_->getNpc() );
            as_->setNpc( pc + delta );
        }
    } 
    else 
    {
      //uint64_t npc = as_->getNpc();
        ctiExec_ = true;
	as_->setAnnulled( i.a() );
        // as_->setPc( npc );
        // as_->setNpc( npc + 4);
	as_->incrPcs();
    }
}

//////////////////////////////////////////////////////

void InstructionEmulator::exec_bpr_core ( InstructionWord &i, int taken ) 
{
    int32_t  disp;
    int64_t  delta;
    uint64_t pc  = as_->getPc();		// temp holding value
  
    //TODO  move to InstructionWord checking
    if( taken == BN ){
	RIESLING_TRAP( Sf_Trap::illegal_instruction );
    }

    // checkTrapBeforeCommit(i);

    if (taken == 1) 
    {
        ctiTaken_ = true;
        if (!pstate_tct_enabled_)
        {
            ctiExec_ = true;
            disp = i.disp16();
            delta = ((disp << 16) >> 14);
            as_->setAnnulled( false );    // cannot annul on taken branch
            as_->setPc( as_->getNpc() );
            as_->setNpc( pc + delta );
        }
    } 
    else 
    {
      // uint64_t npc = as_->getNpc();
        ctiExec_ = true;
        as_->setAnnulled( i.a() );
        // as_->setPc( npc );
        // as_->setNpc( npc + 4);
	as_->incrPcs();
    }
}

//////////////////////////////////////////////////////

int InstructionEmulator::exec_bcc_err ( InstructionWord &i, uint64_t instr_pc)
{
	// FIXME: Riesling trap-handling code here.
// 	RIESLING_TRAP( Sf_Trap::illegal_instruction );
 	return 0;
}

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_bpcc ( InstructionWord &i )
{
	if( i.fmt2CC() == NO_CC2_ICC ) {
	    uint8_t  icc = GET_ICC( ccr_ );
            exec_bpcc_core ( i, br_taken[i.cond()][icc]);
        } else if ( i.fmt2CC() == NO_CC2_XCC ) {
	    uint8_t  xcc = GET_XCC( ccr_ );
            exec_bpcc_core ( i, br_taken[i.cond()][xcc]);
        } 
	return true;
// 	else {
// 	    RIESLING_TRAP( Sf_Trap::illegal_instruction );
//         }
}

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_bicc ( InstructionWord &i )
{
        uint8_t icc = GET_ICC( ccr_ );
	exec_bicc_core ( i, br_taken[i.cond()][icc]);
	return true;
}

//////////////////////////////////////////////////////
#if 0

void EXEC_fbfcc (cpu_structT *sp, uint32_t instr)
{
	uint32_t  fcc;

	if (!FPU_ENABLED ) {
	    trap(sp, T_FP_DISABLED, "FBfccp", use_alternate_globals);
            return;    
	}

	fcc = sp->fsr.s.fcc0;
        exec_bicc_core (sp, instr, fbr_taken[X_COND(instr)][fcc]);
}

//////////////////////////////////////////////////////

void EXEC_fbpfcc (cpu_structT *sp, uint32_t instr)
{
	uint32_t  fcc;

	if (!FPU_ENABLED ) {
	    trap(sp, T_FP_DISABLED, "FBfccp", use_alternate_globals);
            return;    
	}

	switch(X_FMT2_CC(instr)) {
	case CC_FCC0:
		fcc = sp->fsr.s.fcc0;
		break;
	case CC_FCC1:
		fcc = sp->fsr.s.fcc1;
		break;
	case CC_FCC2:
		fcc = sp->fsr.s.fcc2;
		break;
	case CC_FCC3:
		fcc = sp->fsr.s.fcc3;
		break;
	default:
	        trap(sp, T_ILLEGAL_INSTR, "FBfccp - bad CC", use_alternate_globals);
                return;
	}

        exec_bpcc_core (sp, instr, fbr_taken[X_COND(instr)][fcc]);
}
#endif

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_bpr ( InstructionWord &i )
{

    int64_t r1 = ireg_->get( i.rs1() );
    uint8_t  rv = 0;

//     if( i.rcond() == 0 || i.rcond() == 4 ||
// 	i.getNative() >> 28 & 0x01 ){
// 	RIESLING_TRAP( Sf_Trap::illegal_instruction );
//     }

    if      (r1 == 0) {
	rv = 1;
    }
    else if (r1 < 0) {
	rv = 2;
    }
    else if (r1 > 0) {
	rv = 3;
    }

    exec_bpr_core ( i, bpr_taken[i.cond()][rv]);
    return true;
}

// SAM Speedup
//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_sethi ( InstructionWord &i )
{
  ireg_->set( i.rd(), i.imm22() << 10 );
  return true;
}

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_ret_false ( InstructionWord &i )
{
  return false;
}

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_ret_true ( InstructionWord &i )
{
  return true;
}

//////////////////////////////////////////////////////

bool InstructionEmulator::EXEC_RS_illtrap ( InstructionWord &i ) 
{
  RIESLING_THROW_DOMAIN_ERROR( "Internal error" );
  return false;
}
